// Decompiled on 周六 2月 22 19:31:49 CST 2025 with Zomboid Decompiler v0.1.3 using Vineflower.
package zombie.network;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import zombie.Lua.LuaEventManager;
import zombie.core.Core;
import zombie.core.logger.LoggerManager;
import zombie.core.network.ByteBufferWriter;
import zombie.core.raknet.UdpConnection;
import zombie.core.utils.UpdateLimit;
import zombie.debug.DebugLog;
import zombie.gameStates.LoadingQueueState;

public class LoginQueue {
    private static ArrayList<UdpConnection> LoginQueue = new ArrayList();
    private static ArrayList<UdpConnection> PreferredLoginQueue = new ArrayList();
    private static UdpConnection currentLoginQueue;
    private static UpdateLimit UpdateLimit = new UpdateLimit(3050L);
    private static UpdateLimit LoginQueueTimeout = new UpdateLimit(15000L);

    public static void receiveClientLoginQueueRequest(ByteBuffer bb, short packetType) {
        byte _byte = bb.get();
        if (_byte == LoginQueue.LoginQueueMessageType.ConnectionImmediate.ordinal()) {
            LoadingQueueState.onConnectionImmediate();
        } else if (_byte == LoginQueue.LoginQueueMessageType.PlaceInQueue.ordinal()) {
            int _int = bb.getInt();
            LoadingQueueState.onPlaceInQueue(_int);
            LuaEventManager.triggerEvent("OnConnectionStateChanged", "FormatMessage", "PlaceInQueue", _int);
        }

        ConnectionManager.log("receive-packet", "login-queue-request", null);
    }

    public static void receiveLoginQueueDone(ByteBuffer bb, UdpConnection connection, short packetType) {
        long _long = bb.getLong();
        LoggerManager.getLogger("user").write("player " + connection.username + " loading time was: " + _long + " ms");
        synchronized (LoginQueue) {
            if (currentLoginQueue == connection) {
                currentLoginQueue = null;
            }

            loadNextPlayer();
        }

        ConnectionManager.log("receive-packet", "login-queue-done", connection);
        connection.validator.sendChecksum(true, false, false);
    }

    public static void receiveServerLoginQueueRequest(ByteBuffer bb, UdpConnection connection, short packetType) {
        LoggerManager.getLogger("user")
            .write(connection.idStr + " \"" + connection.username + "\" attempting to join used " + (connection.preferredInQueue ? "preferred " : "") + "queue");
        synchronized (LoginQueue) {
            if (!ServerOptions.getInstance().LoginQueueEnabled.getValue()
                || !connection.preferredInQueue && currentLoginQueue == null && PreferredLoginQueue.isEmpty() && LoginQueue.isEmpty()
                || connection.preferredInQueue && currentLoginQueue == null && PreferredLoginQueue.isEmpty()) {
                if (Core.bDebug) {
                    DebugLog.log("receiveServerLoginQueueRequest: ConnectionImmediate (ip:" + connection.ip + ")");
                }

                currentLoginQueue = connection;
                currentLoginQueue.wasInLoadingQueue = true;
                LoginQueueTimeout.Reset((long)(ServerOptions.getInstance().LoginQueueConnectTimeout.getValue() * 1000));
                ByteBufferWriter byteBufferWriter = connection.startPacket();
                PacketTypes.PacketType.LoginQueueRequest2.doPacket(byteBufferWriter);
                byteBufferWriter.putByte((byte)LoginQueue.LoginQueueMessageType.ConnectionImmediate.ordinal());
                PacketTypes.PacketType.LoginQueueRequest2.send(connection);
            } else {
                if (Core.bDebug) {
                    DebugLog.log("receiveServerLoginQueueRequest: PlaceInQueue (ip:" + connection.ip + " preferredInQueue:" + connection.preferredInQueue + ")");
                }

                if (connection.preferredInQueue) {
                    if (!PreferredLoginQueue.contains(connection)) {
                        PreferredLoginQueue.add(connection);
                    }
                } else if (!LoginQueue.contains(connection)) {
                    LoginQueue.add(connection);
                }

                sendPlaceInTheQueue();
            }
        }

        ConnectionManager.log("receive-packet", "login-queue-request", connection);
    }

    private static void sendAccessDenied(UdpConnection udpConnection, String string) {
        if (Core.bDebug) {
            DebugLog.log("sendAccessDenied: (ip:" + udpConnection.ip + " message:" + string + ")");
        }

        ByteBufferWriter byteBufferWriter = udpConnection.startPacket();
        PacketTypes.PacketType.AccessDenied.doPacket(byteBufferWriter);
        byteBufferWriter.putUTF(string);
        PacketTypes.PacketType.AccessDenied.send(udpConnection);
        ConnectionManager.log("access-denied", "invalid-queue", udpConnection);
        udpConnection.forceDisconnect("queue-" + string);
    }

    private static void sendPlaceInTheQueue() {
        for (UdpConnection udpConnection0 : PreferredLoginQueue) {
            ByteBufferWriter byteBufferWriter0 = udpConnection0.startPacket();
            PacketTypes.PacketType.LoginQueueRequest2.doPacket(byteBufferWriter0);
            byteBufferWriter0.putByte((byte)LoginQueue.LoginQueueMessageType.PlaceInQueue.ordinal());
            byteBufferWriter0.putInt(PreferredLoginQueue.indexOf(udpConnection0) + 1);
            PacketTypes.PacketType.LoginQueueRequest2.send(udpConnection0);
        }

        for (UdpConnection udpConnection1 : LoginQueue) {
            ByteBufferWriter byteBufferWriter1 = udpConnection1.startPacket();
            PacketTypes.PacketType.LoginQueueRequest2.doPacket(byteBufferWriter1);
            byteBufferWriter1.putByte((byte)LoginQueue.LoginQueueMessageType.PlaceInQueue.ordinal());
            byteBufferWriter1.putInt(PreferredLoginQueue.size() + LoginQueue.indexOf(udpConnection1) + 1);
            PacketTypes.PacketType.LoginQueueRequest2.send(udpConnection1);
        }
    }

    private static void sendConnectRequest(UdpConnection udpConnection) {
        if (Core.bDebug) {
            DebugLog.log("sendApplyRequest: (ip:" + udpConnection.ip + ")");
        }

        ByteBufferWriter byteBufferWriter = udpConnection.startPacket();
        PacketTypes.PacketType.LoginQueueRequest2.doPacket(byteBufferWriter);
        byteBufferWriter.putByte((byte)LoginQueue.LoginQueueMessageType.ConnectionImmediate.ordinal());
        PacketTypes.PacketType.LoginQueueRequest2.send(udpConnection);
        ConnectionManager.log("send-packet", "login-queue-request", udpConnection);
    }

    public static boolean receiveLogin(UdpConnection connection) {
        if (!ServerOptions.getInstance().LoginQueueEnabled.getValue()) {
            return true;
        } else {
            if (Core.bDebug) {
                DebugLog.log("receiveLogin: (ip:" + connection.ip + ")");
            }

            if (connection != currentLoginQueue) {
                sendAccessDenied(currentLoginQueue, "QueueNotFound");
                if (Core.bDebug) {
                    DebugLog.log("receiveLogin: error");
                }

                return false;
            } else {
                if (Core.bDebug) {
                    DebugLog.log("receiveLogin: ok");
                }

                return true;
            }
        }
    }

    public static void disconnect(UdpConnection connection) {
        if (Core.bDebug) {
            DebugLog.log("disconnect: (ip:" + connection.ip + ")");
        }

        synchronized (LoginQueue) {
            if (connection == currentLoginQueue) {
                currentLoginQueue = null;
            } else {
                if (LoginQueue.contains(connection)) {
                    LoginQueue.remove(connection);
                }

                if (PreferredLoginQueue.contains(connection)) {
                    PreferredLoginQueue.remove(connection);
                }
            }

            sendPlaceInTheQueue();
        }
    }

    public static boolean isInTheQueue(UdpConnection connection) {
        if (!ServerOptions.getInstance().LoginQueueEnabled.getValue()) {
            return false;
        } else {
            synchronized (LoginQueue) {
                return connection == currentLoginQueue || LoginQueue.contains(connection) || PreferredLoginQueue.contains(connection);
            }
        }
    }

    public static void update() {
        if (ServerOptions.getInstance().LoginQueueEnabled.getValue() && UpdateLimit.Check()) {
            synchronized (LoginQueue) {
                if (currentLoginQueue != null) {
                    if (currentLoginQueue.isFullyConnected()) {
                        if (Core.bDebug) {
                            DebugLog.log("update: isFullyConnected (ip:" + currentLoginQueue.ip + ")");
                        }

                        currentLoginQueue = null;
                    } else if (LoginQueueTimeout.Check()) {
                        if (Core.bDebug) {
                            DebugLog.log("update: timeout (ip:" + currentLoginQueue.ip + ")");
                        }

                        currentLoginQueue = null;
                    }
                }

                loadNextPlayer();
            }
        }
    }

    private static void loadNextPlayer() {
        if (!PreferredLoginQueue.isEmpty() && currentLoginQueue == null) {
            currentLoginQueue = (UdpConnection)PreferredLoginQueue.remove(0);
            currentLoginQueue.wasInLoadingQueue = true;
            if (Core.bDebug) {
                DebugLog.log("update: Next player from the preferred queue to connect (ip:" + currentLoginQueue.ip + ")");
            }

            LoginQueueTimeout.Reset((long)(ServerOptions.getInstance().LoginQueueConnectTimeout.getValue() * 1000));
            sendConnectRequest(currentLoginQueue);
            sendPlaceInTheQueue();
        }

        if (!LoginQueue.isEmpty() && currentLoginQueue == null) {
            currentLoginQueue = (UdpConnection)LoginQueue.remove(0);
            currentLoginQueue.wasInLoadingQueue = true;
            if (Core.bDebug) {
                DebugLog.log("update: Next player to connect (ip:" + currentLoginQueue.ip + ")");
            }

            LoginQueueTimeout.Reset((long)(ServerOptions.getInstance().LoginQueueConnectTimeout.getValue() * 1000));
            sendConnectRequest(currentLoginQueue);
            sendPlaceInTheQueue();
        }
    }

    public static String getDescription() {
        return "queue=["
            + LoginQueue.size()
            + "/"
            + PreferredLoginQueue.size()
            + "/\""
            + (currentLoginQueue == null ? "" : currentLoginQueue.getConnectedGUID())
            + "\"]";
    }

    public static enum LoginQueueMessageType {
        ConnectionImmediate,
        PlaceInQueue;
    }
}
